use self::{resp::Message, serial::ModeSConn};
use crate::error::{error_of, AppErr, AppErrMap};
use std::sync::Arc;
use tauri::Window;
use tokio::sync::RwLock;

mod proto;
mod read_handler;
mod resp;
mod serial;
mod write_handler;

static CONN: RwLock<Option<Arc<ModeSConn>>> = RwLock::const_new(None);

#[tauri::command]
pub async fn conn_is_open() -> bool {
    CONN.read().await.is_some()
}

#[tauri::command]
pub fn avaliable_port_names() -> Result<Vec<String>, AppErr> {
    let ports: Vec<String> = tokio_serial::available_ports()
        .map_app()?
        .into_iter()
        .map(|port| port.port_name)
        .collect();

    Ok(ports)
}

#[tauri::command]
pub async fn conn_open(window: Window, name: String) -> Result<(), AppErr> {
    let mut lock = CONN.write().await;
    if lock.is_some() {
        return error_of("串口已经打开");
    }

    let conn = ModeSConn::new(&name, window).await?;

    *lock = Some(conn);

    Ok(())
}

#[tauri::command]
pub async fn conn_close() -> Result<(), AppErr> {
    let mut lock = CONN.write().await;

    match &*lock {
        Some(v) => {
            v.exit();
        }
        None => {
            return error_of("串口已经关闭");
        }
    }
    *lock = None;
    Ok(())
}

#[tauri::command]
pub async fn conn_query(dst: u8, cmd2: u8) -> Result<Message, AppErr> {
    let lock = CONN.read().await;

    match &*lock {
        Some(v) => v.query(dst, cmd2).await,

        None => {
            return error_of("串口已经关闭");
        }
    }
}

#[tauri::command]
pub async fn conn_request(dst: u8, cmd2: u8, data: Box<[u8]>) -> Result<Message, AppErr> {
    let lock = CONN.read().await;
    match &*lock {
        Some(v) => v.request(dst, cmd2, &data).await,

        None => {
            return error_of("串口已经关闭");
        }
    }
}

#[tauri::command]
pub async fn conn_ping(dst: u8) -> Result<(), AppErr> {
    let lock = CONN.read().await;
    match &*lock {
        Some(v) => v.ping(dst).await,

        None => {
            return error_of("串口已经关闭");
        }
    }
}

#[tauri::command]
pub async fn conn_ota(dst: u8, data: Box<[u8]>) -> Result<(), AppErr> {
    let lock = CONN.read().await;
    match &*lock {
        Some(v) => v.ota(dst, &data).await,

        None => {
            return error_of("串口已经关闭");
        }
    }
}
